#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include "../inc/log.h"
#include "../inc/defs.h"

struct logCDT
{
	FILE *	logfile;
	levelT	currentlevel;
};

static logADT	log = NULL;

static void writeLog( logADT log, levelT FATAL, const char * logmsg, va_list ap );
static char * Header(levelT level);

logADT 
NewLog(const char * logname)
{
	FILE * logfile;
	
	if ( Precondicion(logname) )
		return NULL;
	
	if (log != NULL)
		return log;
		
	if ( (log = calloc(1, sizeof(struct logCDT))) == NULL )
		return NULL;
	
	if( (strcmp(logname, "stdout") != 0 ) )
	{
		if ( (logfile = fopen(logname, "w+t")) == NULL )
		{
			free(log);
			return NULL;
		}
	}
	else
		logfile = stdout;
		
	log -> logfile = logfile;
	
	return log;
}
	
logADT 
OpenLog(const char * logname)
{
	FILE * logfile;
	
	if ( Precondicion(logname) )
		return NULL;
	
	if (log != NULL)
		return log;
		
	if ( (log = calloc(1, sizeof(struct logCDT))) == NULL )
		return NULL;
	
	if ( (logfile = fopen(logname, "at")) == NULL )
	{
		free(log);
		return NULL;
	}
		
	log -> logfile = logfile;
	
	return log;
}

void 
SetLogLevel(logADT log, levelT level)
{
	if ( Precondicion(log) || level < 0 || level > FATAL )
	{
		fprintf(stderr, "Invalid arguments to function SetLogLevel\n");
		return;
	}
	
	log -> currentlevel = level;
	
	return;
}

void 
Fatal(const char * logmsg, ...)
{
    if (Precondicion(log) || Precondicion( logmsg ))
    {
    	Error("Invalid arguments to function Fatal\n");
		return;
    }

	va_list ap;
    va_start( ap, logmsg );

    writeLog( log, FATAL, logmsg, ap );

    va_end( ap );

    return;
}

void
Error(const char * logmsg, ...)
{
	if ( Precondicion(log) )
	{
		fprintf(stderr, "Invalid log instance, log was not initialized properly\n");
		return;
	}
	
    if (Precondicion( logmsg ))
    {
    	Error("Invalid arguments to function Error\n");
    	return;
    }

	va_list ap;
    va_start( ap, logmsg );

    writeLog( log, ERROR, logmsg, ap );

    va_end( ap );

    return;
}

void
Warning(const char * logmsg, ...)
{
    if (Precondicion(log) || Precondicion( logmsg ))
    {
    	Error("Invalid arguments to function Error\n");
    	return;
    }
	
	va_list ap;
    va_start( ap, logmsg );
	
    writeLog( log, WARNING, logmsg, ap );

    va_end( ap );

    return;
}

void 
Notification(const char * logmsg, ...)
{
	if (Precondicion(log) || Precondicion(logmsg) )
	{
		Error("Invalid arguments to function Notification\n");
		return;
	}
	
	va_list ap;
    va_start( ap, logmsg );
	
	writeLog(log, NOTIFICATION, logmsg, ap);
	
	va_end(ap);
	
	return;
}

void 
Debug(const char * logmsg, ...)
{
	if (Precondicion(log) || Precondicion(logmsg) )
	{
		Error("Invalid arguments to function Debug\n");
		return;
	}

	va_list ap;
    va_start( ap, logmsg );
	
	writeLog(log, DEBUG, logmsg, ap);
	
	va_end(ap);
	
	return;
}

void 
CloseLog(logADT log)
{
	if ( Precondicion(log) )
	{
		Error("Invalid arguments to function CloseLog\n");
		return;
	}
	
	fclose(log -> logfile);
	log -> logfile = NULL;
	free(log);
	log = NULL;
	return;
}

static void
writeLog( logADT log, levelT level, const char * logmsg, va_list ap )
{
	if ( level > log -> currentlevel )
	{
		fprintf(stderr, "Could not write to log, logging detail level is currently set to a lower value\n");
		return;
	}
	
	fprintf(log -> logfile, "%s: ", Header(level) );
	vfprintf(log -> logfile, logmsg, ap);
	return;
}

static char *
Header(levelT level)
{
	char * headers[5] = {"Debug", "Notification", "Warning", "Error", "Fatal"};
	
	return headers[level];
}

